package ma.xin.shi;

import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.widgets.TableItem;

import java.util.ArrayList;
import java.util.List;

public class AutorsarCommonUtils {

	/**
     * 打印日志或调试信息
     */
    public static void log(String message) {
        System.out.println("[ARXML Parser] " + message);
    }
	
	private static final boolean ENABLE_CALLER_INFO = true; // 调试时开启
    public static void logEx(String message) {
        String callerInfo = "";
        if (ENABLE_CALLER_INFO) {
            StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
            String simpleName = caller.getClassName();
            simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1);
            callerInfo = String.format("[%s.%s:%d] ", simpleName, caller.getMethodName(), caller.getLineNumber());
        }
        System.out.println("[mxsDbg]" + callerInfo + message);
    }
	
    // 默认最小长度
    public static final int DEFAULT_MIN_LENGTH = 0;

    // 默认最大长度
    public static final int DEFAULT_MAX_LENGTH = 255;

    // 枚举示例：基础类型
    public enum ServiceRPCType {
    	RPCTypeEvent, RPCTypeField, RPCTypeMethod
    }

    public enum CurrentRPCType {
    	CurrentRPCEvent, CurrentRPCField, CurrentRPCMethod
    }
    /**
     * 获取所有基础类型的名称数组
     */
    public static String testGetBaseTypeNames() {
        
        return "maxinshi";
    }

    public enum ServiceGUISet {
    	SERVICE_GUI_LIST, SERVICE_GUI_EVENT, SERVICE_GUI_FIELD, SERVICE_GUI_METHOD
    }
    
    public enum ServiceConmfirmButtonSt {
    	SERVICE_BT_ADD_SVC, SERVICE_BT_SAVE_SVC, SERVICE_BT_ADD_RPC, SERVICE_BT_SAVE_RPC
    }

    public static  String getSafeText(String value) {
        // 如果值为null，返回空字符串；否则返回原值
        return value == null ? "" : value;
    }
    
    public static  void setSafeText(Text text, String value) {
        if(value != null) {
        	text.setText(value);
        } else {
        	logEx("You wanna set text to null");
        }
    }
    
    public static  void setSafeCombo(Combo text, String value) {
        if(value != null) {
        	text.setText(value);
        } else {
        	logEx("You wanna set text to null");
        }
    }
    
    public static Combo createLabeledCombo(Composite parent, String labelText, String[] items) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Combo combo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
        combo.setItems(items);
        combo.select(0); // 默认选第一个
        combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        return combo;
    }

    public static Spinner createLabeledSpinner(Composite parent, String labelText, int min, int max) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Spinner spinner = new Spinner(parent, SWT.BORDER);
        spinner.setMinimum(min);
        spinner.setMaximum(max);
        spinner.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        return spinner;
    }

    public static Table createLabeledTable(Composite parent, String labelText, String[] columnNames, int style) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Table table = new Table(parent, style);
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        for (String columnName : columnNames) {
            TableColumn column = new TableColumn(table, SWT.NONE);
            column.setText(columnName);
        }

        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        return table;
    }
    
    public static Button createLabeledCheckbox(Composite parent, String labelText) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Button checkbox = new Button(parent, SWT.CHECK);
        checkbox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        return checkbox;
    }

    public static Text createLabeledReadOnly(Composite parent, String labelText, String defaultValue) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Text text = new Text(parent, SWT.BORDER | SWT.READ_ONLY);
        text.setText(defaultValue);
        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        return text;
    }

    public static Text createLabeledText(Composite parent, String labelText) {
        Label label = new Label(parent, SWT.NONE);
        label.setText(labelText);

        Text text = new Text(parent, SWT.BORDER);
        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        return text;
    }

    /**
     * 在容器右下角创建两个按钮（如 Add / Cancel）
     *
     * @param parent 父容器
     * @param text1 第一个按钮的文本
     * @param text2 第二个按钮的文本
     * @return Button[] 两个按钮对象
     */
    public static Button[] createButtonsAtBottomRight(Composite parent, String text1, String text2) {
        Composite buttonContainer = new Composite(parent, SWT.NONE);
        RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
        rowLayout.spacing = 5;
        rowLayout.marginTop = 5;
        rowLayout.marginBottom = 5;
        buttonContainer.setLayout(rowLayout);

        GridData containerData = new GridData(SWT.END, SWT.BOTTOM, false, false);
        containerData.horizontalSpan = 2; // 如果是两列布局
        buttonContainer.setLayoutData(containerData);

        Button button1 = new Button(buttonContainer, SWT.PUSH);
        button1.setText(text1);

        Button button2 = new Button(buttonContainer, SWT.PUSH);
        button2.setText(text2);

        return new Button[] { button1, button2 };
    }

    /**
     * 在容器左上角创建两个按钮（如 Refresh / Export）
     *
     * @param parent 父容器
     * @param text1 第一个按钮的文本
     * @param text2 第二个按钮的文本
     * @return Button[] 两个按钮对象
     */
    public static Button[] createButtonsAtTopLeft(Composite parent, String text1, String text2) {
        Composite buttonContainer = new Composite(parent, SWT.NONE);
        RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
        rowLayout.spacing = 5;
        rowLayout.marginTop = 5;
        rowLayout.marginBottom = 5;
        buttonContainer.setLayout(rowLayout);

        GridData containerData = new GridData(SWT.FILL, SWT.TOP, true, false);
        containerData.horizontalSpan = 2;
        buttonContainer.setLayoutData(containerData);

        Button button1 = new Button(buttonContainer, SWT.PUSH);
        button1.setText(text1);

        Button button2 = new Button(buttonContainer, SWT.PUSH);
        button2.setText(text2);

        return new Button[] { button1, button2 };
    }

    /**
     * 创建带 Label 的多选表格控件
     *
     * @param parent       父容器
     * @param labelText    Label 文本
     * @param columnNames  列名（目前只支持单列）
     * @param initialData  初始化数据列表
     * @return CheckboxTableViewer 可用于后续获取/设置选中项
     
     call example:
     	List<String> ECUList = Arrays.asList("VechileBody", "VCC_MCU", "VCC_MPU");
		CheckboxTableViewer viewer = AutorsarCommonUtils.createMultiSelectTable(
		        parent,
		        "Server Select：",
		        new String[]{"ECU Node"},
		        ECUList
		);
     
     */
    
    public static CheckboxTableViewer createMultiSelectTable(
            Composite parent,
            String labelText,
            String[] columnNames,
            List<String> initialData) {

        // 创建一个子容器用于布局
        Composite container = new Composite(parent, SWT.NONE);
        container.setLayout(new GridLayout(2, false));
        container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // 添加 Label
        Label label = new Label(container, SWT.NONE);
        label.setText(labelText);

        // 占位用的空 Label，让表格独占一行
        new Label(container, SWT.NONE);

        // 创建 CheckboxTableViewer
        CheckboxTableViewer tableViewer = CheckboxTableViewer.newCheckList(container, SWT.BORDER | SWT.FULL_SELECTION);

        // 设置表格填充整个宽度
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        gridData.horizontalSpan = 2;
        tableViewer.getControl().setLayoutData(gridData);

        // 设置内容提供者（使用标准 ArrayContentProvider）
        tableViewer.setContentProvider(ArrayContentProvider.getInstance());

        // 设置标签提供者（显示字符串）
        tableViewer.setLabelProvider(new LabelProvider() {
            @Override
            public String getText(Object element) {
                return element.toString();
            }
        });

        // 设置输入内容
        if (initialData != null && !initialData.isEmpty()) {
            tableViewer.setInput(initialData);
        }

        return tableViewer;
    }
    
    public static void setLabelFontStyle(Label label, int style) {
        if (label == null || label.isDisposed()) return;

        Font currentFont = label.getFont();
        FontData[] fontData = currentFont.getFontData();

        for (FontData data : fontData) {
            data.setStyle(style);
        }

        Font newFont = new Font(label.getDisplay(), fontData);
        label.setFont(newFont);

        label.addDisposeListener(e -> newFont.dispose());
    }

    /**
     * 将字符串转换为int，支持十进制和0x/0X开头的十六进制格式
     * @param str        输入字符串（支持十进制如"100"或十六进制如"0xFF"）
     * @param defaultVal 转换失败时的默认值
     * @return 转换后的int值，或默认值
     */
    public static int parseInteger(String str, int defaultVal) {
        if (str == null || str.isEmpty()) {
            return defaultVal;
        }

        String trimmed = str.trim();

        try {
            // 处理十六进制格式（0x或0X开头）
            if (trimmed.length() > 2 && 
                (trimmed.startsWith("0x") || trimmed.startsWith("0X"))) {
                return Integer.parseInt(trimmed.substring(2), 16);
            }

            // 处理普通十进制格式
            return Integer.parseInt(trimmed);
        } catch (NumberFormatException e) {
            System.err.printf("字符串转int失败（无效格式）: %s%n", str);
            return defaultVal;
        } catch (IllegalArgumentException e) {
            // 处理超出Integer范围的情况（如0x80000000）
            System.err.printf("字符串转int失败（数值超出范围）: %s%n", str);
            return defaultVal;
        }
    }

    public static void refreshTableIndex(Table tbl) {
	    TableItem[] items = tbl.getItems();
	    for (int i = 0; i < items.length; i++) {
	        items[i].setText(0, String.valueOf(i + 1));
	    }
	}
    
    /**
     * 移除字符串中的指定前缀（仅移除一次，区分大小写）
     * @param original 原始字符串（如"SOMEIP_aaa"）
     * @param prefix 要移除的前缀（如"SOMEIP_"）
     * @return 移除前缀后的字符串；若原始字符串为null或不包含前缀，则返回原始字符串
     */
    public static String removePrefix(String original, String prefix) {
        // 处理null情况
        if (original == null || prefix == null) {
            return original;
        }
        
        // 检查字符串是否以指定前缀开头
        if (original.startsWith(prefix)) {
            // 从前缀长度之后截取字符串
            return original.substring(prefix.length());
        }
        
        // 不包含前缀则返回原字符串
        return original;
    }
    
    /**
     * 移除字符串中的指定后缀（仅移除一次，区分大小写）
     * @param original 原始字符串（如"aaa_SOMEIP"）
     * @param suffix 要移除的后缀（如"_SOMEIP"）
     * @return 移除后缀后的字符串；若原始字符串为null或不包含后缀，则返回原始字符串
     */
    public static String removeSuffix(String original, String suffix) {
        // 处理null情况
        if (original == null || suffix == null) {
            return original;
        }
        
        // 避免后缀长度大于原始字符串导致的异常
        if (suffix.length() > original.length()) {
            return original;
        }
        
        // 检查字符串是否以指定后缀结尾
        if (original.endsWith(suffix)) {
            // 截取到后缀开始位置之前的部分
            return original.substring(0, original.length() - suffix.length());
        }
        
        // 不包含后缀则返回原字符串
        return original;
    }

    /**
     * 获取原始字符串中第一个特定字符之前的内容
     * @param original 原始字符串（如"aaa_haha_hou"）
     * @param separator 特定分隔字符（如'_'）
     * @return 第一个分隔符之前的子串；若字符串为null、无分隔符或分隔符在开头，则返回原字符串
     */
    public static String getBeforeFirstSeparator(String original, char separator) {
        if (original == null) {
            return null;
        }
        
        int index = original.indexOf(separator);
        // 未找到分隔符或分隔符在首位（index=0），返回原字符串
        if (index <= 0) {
            return original;
        }
        
        // 截取从开头到分隔符之前的部分
        return original.substring(0, index);
    }

    /**
     * 获取原始字符串中最后一个特定字符之后的内容
     * @param original 原始字符串（如"aaa_haha_hou"）
     * @param separator 特定分隔字符（如'_'）
     * @return 最后一个分隔符之后的子串；若字符串为null、无分隔符或分隔符在末尾，则返回原字符串
     */
    public static String getAfterLastSeparator(String original, char separator) {
        if (original == null) {
            return null;
        }
        
        int index = original.lastIndexOf(separator);
        // 未找到分隔符或分隔符在末尾（index=length-1），返回原字符串
        if (index == -1 || index == original.length() - 1) {
            return original;
        }
        
        // 截取从分隔符之后到结尾的部分
        return original.substring(index + 1);
    }
    
    /**
     * 获取原始字符串中两个标记字符串之间的内容，确保第二个标记在第一个标记之后
     * @param original 原始字符串（如"EthConnector_VLAN62_TBOX_VLAN78"）
     * @param marker1 第一个标记字符串（如"VLAN"）
     * @param marker2 第二个标记字符串（如"_"）
     * @return 两个标记之间的子串；若不符合条件则返回原字符串
     * 
     *  使用示例
			public static void main(String[] args) {
			    String testStr1 = "EthConnector_VLAN62_TBOX";
			    System.out.println(getBetweenTwoMarkers(testStr1, "VLAN", "_"));  // 输出: 62
			    
			    String testStr2 = "_VLAN62_TBOX_VLAN78_";
			    System.out.println(getBetweenTwoMarkers(testStr2, "VLAN", "_"));  // 输出: 62（忽略第一个标记前的"_"）
			    
			    String testStr3 = "VLAN123_abc_VLAN456_def";
			    System.out.println(getBetweenTwoMarkers(testStr3, "VLAN", "_"));  // 输出: 123（只取第一个标记后的内容）
			    
			    // 测试边缘情况
			    System.out.println(getBetweenTwoMarkers(null, "VLAN", "_"));  // null
			    System.out.println(getBetweenTwoMarkers("VLAN123", "VLAN", "_"));  // VLAN123（第二个标记不存在）
			    System.out.println(getBetweenTwoMarkers("abc_VLAN", "VLAN", "_"));  // abc_VLAN（第一个标记在末尾）
			}
     */
    public static String getBetweenTwoMarkers(String original, String marker1, String marker2) {
        // 处理原始字符串为null的情况
        if (original == null) {
            return null;
        }
        
        // 处理标记字符串为null的情况
        if (marker1 == null || marker2 == null) {
            return original;
        }
        
        // 查找第一个标记的起始位置
        int marker1Start = original.indexOf(marker1);
        // 第一个标记不存在，返回原字符串
        if (marker1Start == -1) {
            return original;
        }
        
        // 计算第一个标记的结束位置（起始位置 + 标记1的长度）
        int marker1End = marker1Start + marker1.length();
        // 第一个标记在字符串末尾，返回原字符串
        if (marker1End >= original.length()) {
            return original;
        }
        
        // 从第一个标记结束位置开始查找第二个标记，确保在第一个标记之后
        int marker2Start = original.indexOf(marker2, marker1End);
        // 第二个标记在第一个标记之后不存在，返回原字符串
        if (marker2Start == -1) {
            return original;
        }
        
        // 截取两个标记之间的内容
        return original.substring(marker1End, marker2Start);
    }
    
    /**
     * 获取字符串中最后一个指定子串后面的内容
     * 
     * @param str 源字符串（若为null则返回null）
     * @param substring 要查找的子串（若为null或空字符串，返回源字符串）
     * @return 最后一个指定子串后面的内容；若未找到子串，返回源字符串；
     *         若子串位于字符串末尾，返回空字符串
     */
    public static String getSubstringAfterLast(String str, String substring) {
        // 处理源字符串为null的情况
        if (str == null) {
            return null;
        }
        
        // 处理子串为null或空字符串的情况
        if (substring == null || substring.isEmpty()) {
            return str;
        }
        
        // 查找子串最后一次出现的位置
        int lastIndex = str.lastIndexOf(substring);
        
        // 若未找到子串，返回源字符串
        if (lastIndex == -1) {
            return str;
        }
        
        // 计算子串后面内容的起始位置
        int startIndex = lastIndex + substring.length();
        
        // 若子串在字符串末尾，返回空字符串
        if (startIndex >= str.length()) {
            return "";
        }
        
        // 返回子串后面的内容
        return str.substring(startIndex);
    }

    /**
     * 比较两个字符串相同长度的部分是否完全相同
     * @param str1 第一个字符串
     * @param str2 第二个字符串
     * @return 相同长度部分是否完全相同
     */
    public static boolean compareSameLengthPart(String str1, String str2) {
        // 处理null情况
        if (str1 == null || str2 == null) {
            return str1 == str2; // 都为null返回true，否则false
        }
        
        // 获取两个字符串中较短的长度
        int minLength = Math.min(str1.length(), str2.length());
        
        // 比较相同长度部分的每个字符
        for (int i = 0; i < minLength; i++) {
            if (str1.charAt(i) != str2.charAt(i)) {
                return false;
            }
        }
        
        // 所有相同位置的字符都匹配
        return true;
    }

    /**
     * 毫秒（ms）转换为秒（s）的工具接口，返回去除小数点后末尾所有0的字符串
     * @param ms 输入的非负毫秒数（如1、30），符合工程时间数据逻辑
     * @return 转换后的秒数字符串（如输入1返回"0.001"，输入30返回"0.03"，输入1500返回"1.5"）
     * @throws IllegalArgumentException 若输入毫秒数为负数，抛出非法参数异常（匹配工具类健壮性需求）
     */
    public static String convertMsToS(int ms) {
        // 1. 参数合法性校验：禁止负毫秒数（工程场景时间不可为负）
        if (ms < 0) {
            String errorMsg = String.format("输入毫秒数非法：%d（不允许为负数）", ms);
            logEx(errorMsg); // 复用已有日志方法记录错误上下文
            throw new IllegalArgumentException(errorMsg);
        }

        // 2. 核心转换逻辑：1秒=1000毫秒，先计算原始秒数
        double seconds = ms / 1000.0;

        // 3. 格式处理：去除小数点后末尾所有0，避免冗余（如0.030→"0.03"，1.500→"1.5"，2.0→"2"）
        String resultStr = String.valueOf(seconds);
        // 若包含小数点，去除末尾0；若去除后仅剩小数点，则去掉小数点（如2.0→"2"）
        if (resultStr.contains(".")) {
            resultStr = resultStr.replaceAll("0+$", "").replaceAll("\\.$", "");
        }

        // 4. 调试日志：记录转换过程（输入-输出映射，便于追溯）
        String logMsg = String.format("毫秒转秒完成：输入%d ms → 输出%s s", ms, resultStr);
        logEx(logMsg);

        return resultStr;
    }

    /**
     * 毫秒（ms）转换为秒（s）的工具接口，返回去除小数点后末尾所有0的浮点数（float类型）
     * @param ms 输入的非负毫秒数（如1、30、1500），符合工程时间数据逻辑（匹配需求中“工程配置时间处理”场景）
     * @return 转换后的秒数（float类型，如输入1返回0.001f，输入30返回0.03f，输入1500返回1.5f，输入2000返回2.0f）
     * @throws IllegalArgumentException 若输入毫秒数为负数，抛出非法参数异常（保障工具类健壮性，符合需求隐含的“数据合法性校验”要求）
     */
    public static float convertMsToSFloat(int ms) {
        // 1. 参数合法性校验：复用原接口校验逻辑，禁止负毫秒数（工程场景时间不可为负）
        if (ms < 0) {
            String errorMsg = String.format("输入毫秒数非法：%d（不允许为负数）", ms);
            logEx(errorMsg); // 复用已有日志方法，记录错误上下文（含调用类、方法、行号）
            throw new IllegalArgumentException(errorMsg);
        }

        // 2. 核心转换逻辑：1秒=1000毫秒，先计算原始秒数（用double确保转换精度，再强转为float）
        double originalSeconds = ms / 1000.0;
        // 关键：通过字符串中间处理去除末尾冗余0，再解析为float（避免直接强转导致的0.030f等冗余格式）
        String tempStr = String.valueOf(originalSeconds);
        if (tempStr.contains(".")) {
            tempStr = tempStr.replaceAll("0+$", "").replaceAll("\\.$", "");
        }
        float resultFloat = Float.parseFloat(tempStr);

        // 3. 调试日志：复用日志能力，记录“输入-输出”映射（便于工程调试与问题追溯，符合需求隐含的“可追溯性”要求）
        String logMsg = String.format("毫秒转秒（浮点数）完成：输入%d ms → 输出%.6f s（float类型：%s）", 
                                     ms, originalSeconds, resultFloat);
        logEx(logMsg);

        return resultFloat;
    }
    
    /**
     * 将字符串转换为boolean值
     * 支持"true"（不区分大小写）转换为true，其他字符串（包括"false"）转换为false
     * @param str 输入的字符串（可为null，null将返回false）
     * @return 转换后的boolean值
     */
    public static boolean stringToBoolean(String str) {
        // 处理null情况，返回false
        if (str == null) {
            return false;
        }
        // 忽略大小写判断是否为"true"
        return "true".equalsIgnoreCase(str.trim());
    }

    /**
     * 将boolean值转换为字符串
     * true转换为"true"，false转换为"false"
     * @param value 输入的boolean值
     * @return 转换后的字符串（"true"或"false"）
     */
    public static String booleanToString(boolean value) {
        return value ? "true" : "false";
    }
    
    /**
     * 从被特殊字符分隔的字符串中提取指定段内容
     * 特性：无论字符串开头是否有分隔符，都能得到一致的分段结果
     * @param source 原始字符串（如"/Service/xxx"或"Service/xxx"）
     * @param separator 分隔字符（如'/'）
     * @param segmentNumber 要获取的段数（从1开始计数）
     * @return 第segmentNumber段的字符串内容
     * @throws IllegalArgumentException 当原始字符串为null、段数小于1或超出实际段数时抛出
     */
    public static String getSegmentBySeparator(String source, char separator, int segmentNumber) {
        // 校验输入参数合法性
        if (source == null) {
            throw new IllegalArgumentException("原始字符串不能为null");
        }
        if (segmentNumber < 1) {
            throw new IllegalArgumentException("段数必须大于等于1：" + segmentNumber);
        }
        if (source.isEmpty()) {
            throw new IllegalArgumentException("原始字符串不能为空");
        }
        
        // 分割字符串（使用split方法，保留所有分割结果包括空串）
        String[] segments = source.split(String.valueOf(separator), -1);
        
        // 过滤空字符串，得到有效段列表（解决开头/中间/结尾分隔符导致的空串问题）
        List<String> validSegments = new ArrayList<>(segments.length);
        for (String segment : segments) {
            if (!segment.isEmpty()) {
                validSegments.add(segment);
            }
        }
        
        // 检查请求的段数是否超出有效段数量
        if (segmentNumber > validSegments.size()) {
            throw new IllegalArgumentException(
                String.format("段数超出范围，实际有效段数为%d，请求第%d段", 
                              validSegments.size(), segmentNumber)
            );
        }
        
        // 返回指定段（段数从1开始，对应列表的0索引）
        return validSegments.get(segmentNumber - 1);
    }
}
